﻿using Neoit.Utils.DataModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Neoit.Utils.Office
{
    /// <summary>
    /// 表数据帮助类
    /// </summary>
    public static class TableHelper
    {
        #region 二维字符串集合转实体集合
        /// <summary>
        /// 二维string集合转换为实体集合
        /// 前置条件：若有title，则title和content是相邻的
        /// 实现核心功能，避免过度封装
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="sourceDatas">数据源</param>
        /// <param name="columnInfos">属性和列对应关系</param>
        /// <param name="titleContentStartIndex">数据起始索引（若有标题，则应为标题的index）</param>
        /// <returns>T集合</returns>
        public static List<T> ConvertToEntity<T>(IEnumerable<IEnumerable<string>> sourceDatas, IEnumerable<MapInfo> columnInfos, int titleContentStartIndex = 0) where T : new()
        {
            #region 错误提示
            if (sourceDatas.ToList().GetRange(titleContentStartIndex, sourceDatas.Count() - titleContentStartIndex).Select(s => s.Count()).Distinct().Count() != 1) throw new ArgumentException("数据源[sourceDatas]应是一个等长的集合");
            #endregion
            var list = new List<T>();
            // 设置了属性名与数据源标题对应关系、否则按columnInfos属性顺序赋值
            IEnumerable<string> sourceTitle = null;
            var indexContent = titleContentStartIndex;
            if (columnInfos.Any(s => s.Title != null))//若MapInfo中title有值，则视第一条为标题行，其余为业务数据；否则全视为业务数据；
            {
                sourceTitle = sourceDatas.ElementAt(titleContentStartIndex);
                indexContent = titleContentStartIndex + 1;
            }
            sourceDatas = sourceDatas.ToList().GetRange(indexContent, sourceDatas.Count() - indexContent); //Start Row
            foreach (var item in sourceDatas)
            {
                var rowData = BindRowData<T>(item, columnInfos, sourceTitle);
                list.Add(rowData);
            }
            return list;
        }
        public static T BindRowData<T>(IEnumerable<string> sourceRow, IEnumerable<MapInfo> columnInfo, IEnumerable<string> sourceTitle) where T : new()
        {
            var entity = new T();
            var properties = entity.GetType().GetProperties();
            foreach (var item in columnInfo)
            {
                var pi = properties.FirstOrDefault(s => s.Name == item.PropertyName);
                var index = columnInfo.ToList().IndexOf(item);//顺序索引
                if (sourceTitle != null)
                {
                    if (item.Title != null)//使用标题映射
                    {
                        index = item.MatchType switch
                        {
                            MatchType.Contains => sourceTitle.ToList().FindIndex(s => s.Contains(item.Title)),
                            MatchType.Equal => sourceTitle.ToList().FindIndex(s => s == item.Title),
                            MatchType.Regex => sourceTitle.ToList().FindIndex(s => Regex.IsMatch(s, item.Title)),
                            MatchType.RegexIgnoreCase => sourceTitle.ToList().FindIndex(s => Regex.IsMatch(s, item.Title,RegexOptions.IgnoreCase)),
                            _ => -1
                        };
                    }
                    else//使用指定索引映射
                    {
                        index = Convert.ToInt32(item.Index);
                    }
                }
                else
                {
                    if (item.Index != null) index = Convert.ToInt32(item.Index);//数据源中无标题、且指定了index值：使用指定索引映射
                }
                if (index == -1) continue;
                pi.SetValue(entity, Convert.ChangeType(sourceRow.ElementAt(index), pi.PropertyType));
            }
            return entity;
        }
        /// <summary>
        /// 二维string集合转换为实体集合
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="sourceDatas">数据源</param>
        /// <param name="columnInfoDic">{属性名:列标题}</param>
        /// <param name="startRowIndex">起始索引</param>
        /// <returns>T集合</returns>
        public static List<T> ConvertToEntity<T>(IEnumerable<IEnumerable<string>> sourceDatas, Dictionary<string, string> columnInfoDic, int startRowIndex = 0) where T : new()
        {
            var columnInfos = columnInfoDic.Select(s => new MapInfo(s.Key, s.Value, MatchType.Equal)).ToList();
            return ConvertToEntity<T>(sourceDatas, columnInfos, startRowIndex);
        }
        /// <summary>
        /// 二维string集合转换为实体集合 默认MatchType=contains
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="sourceDatas">数据源</param>
        /// <param name="columnInfoDic">属性名和列标题对应关系</param>
        /// <param name="startRowIndex">起始索引</param>
        /// <returns>T集合</returns>
        public static List<T> ConvertToEntityLike<T>(IEnumerable<IEnumerable<string>> sourceDatas, Dictionary<string, string> columnInfoDic, int startRowIndex = 0) where T : new()
        {
            var columnInfos = columnInfoDic.Select(s => new MapInfo(s.Key, s.Value, MatchType.Contains)).ToList();
            return ConvertToEntity<T>(sourceDatas, columnInfos, startRowIndex);
        }
        /// <summary>
        /// 二维string集合转换为实体集合 默认MatchType=regex
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="sourceDatas">数据源</param>
        /// <param name="columnInfoDic">属性名和列标题对应关系</param>
        /// <param name="startRowIndex">起始索引</param>
        /// <returns>T集合</returns>
        public static List<T> ConvertToEntityRegex<T>(IEnumerable<IEnumerable<string>> sourceDatas, Dictionary<string, string> columnInfoDic, int startRowIndex = 0) where T : new()
        {
            var columnInfos = columnInfoDic.Select(s => new MapInfo(s.Key, s.Value, MatchType.Regex)).ToList();
            return ConvertToEntity<T>(sourceDatas, columnInfos, startRowIndex);
        }
        /// <summary>
        /// 二维string集合转换为实体集合 默认MatchType=RegexIgnoreCase
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="sourceDatas">数据源</param>
        /// <param name="columnInfoDic">属性名和列标题对应关系</param>
        /// <param name="startRowIndex">起始索引</param>
        /// <returns>T集合</returns>
        public static List<T> ConvertToEntityRegexIgnoreCase<T>(IEnumerable<IEnumerable<string>> sourceDatas, Dictionary<string, string> columnInfoDic, int startRowIndex = 0) where T : new()
        {
            var columnInfos = columnInfoDic.Select(s => new MapInfo(s.Key, s.Value, MatchType.RegexIgnoreCase)).ToList();
            return ConvertToEntity<T>(sourceDatas, columnInfos, startRowIndex);
        }
        /// <summary>
        /// 二维string集合转换NameStringValue：默认name index=0;value index=1
        /// </summary>
        /// <param name="sourceDatas">数据源</param>
        /// <returns>T集合</returns>
        public static List<NameStringValue> ConvertToNameStringValue(IEnumerable<IEnumerable<string>> sourceDatas)
        {
            return ConvertToEntity<NameStringValue>(sourceDatas, new List<MapInfo> { new MapInfo(nameof(NameStringValue.Name)), new MapInfo(nameof(NameStringValue.Value)) });
        }

        #endregion

        #region 查找指定内容的索引
        /// <summary>
        /// 查找指定内容的索引
        /// </summary>
        /// <param name="list"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        public static (int rowIndex, int colIndex) FindIndexs(this List<List<string>> list, string target)
        {
            for (int i = 0; i < list.Count; i++)
            {
                for (int j = 0; j < list[i].Count; j++)
                {
                    if (list[i][j] == target) return (i, j);
                }
            }
            return (-1, -1);
        }
        #endregion
    }
    /// <summary>
    /// 实体属性名和数据列对应模型
    /// </summary>
    public class MapInfo
    {
        /// <summary>
        /// 属性名==数据顺序索引
        /// </summary>
        /// <param name="propertyName">属性名：nameof(xx.yy)</param>
        public MapInfo(string propertyName)
        {
            PropertyName = propertyName;
        }
        /// <summary>
        /// 属性名==指定数据索引
        /// </summary>
        /// <param name="propertyName">属性名：nameof(xx.yy)</param>
        /// <param name="index"></param>
        public MapInfo(string propertyName, int? index)
        {
            PropertyName = propertyName;
            Index = index;
        }
        /// <summary>
        /// 属性名==数据标题，默认MatchType为equal
        /// </summary>
        /// <param name="propertyName">属性名：nameof(xx.yy)</param>
        /// <param name="title">数据标题</param>
        /// <param name="matchType">默认使用MatchType.equal</param>
        public MapInfo(string propertyName, string title, MatchType matchType = MatchType.Equal)
        {
            PropertyName = propertyName;
            Title = title;
            MatchType = matchType;
        }
        /// <summary>
        /// 对象属性名：nameof(xx.yy)
        /// </summary>
        public string PropertyName { get; set; }
        /// <summary>
        /// 数据标题
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 匹配类型
        /// </summary>
        public MatchType MatchType { get; set; }
        /// <summary>
        /// 若无标题则使用列索引对应
        /// </summary>
        public int? Index { get; set; }
    }
    /// <summary>
    /// 实体属性名和列标题匹配模式
    /// </summary>
    public enum MatchType
    {
        /// <summary>
        /// 完全相同
        /// </summary>
        Equal,
        /// <summary>
        /// contains包含
        /// </summary>
        Contains,
        /// <summary>
        /// 正则匹配
        /// </summary>
        Regex,
        /// <summary>
        /// 正则匹配
        /// </summary>
        RegexIgnoreCase
    }

}
